`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          OPRD_BW                 = 8;    // bit width of single operand
localparam          OPRD_NUM                =127;   // number of operands, must 2<=OPRD_NUM<=128
localparam          SUM_BW                  = OPRD_BW+$clog2(OPRD_NUM);
localparam          DIV32_NUM               = (OPRD_BW*OPRD_NUM-1)/32+1;

reg                                         rst_n;
reg                                         clk;

reg                                         vld_in;
reg                 [OPRD_BW*OPRD_NUM-1:0]  oprds;
wire                [SUM_BW-1:0]            sum_o;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_add_all", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_OPRDS
    reg         [DIV32_NUM*32-1:0]      rnd;
    integer                             i;

    vld_in = 1'b0;
    @(posedge rst_n);

    repeat(10_000) begin
        @(posedge clk);

        for (i=0; i<DIV32_NUM; i=i+1) begin
            vld_in =`U_DLY 1'b1;
            rnd[i*32+:32] =`U_DLY $urandom();
        end
        oprds =`U_DLY rnd[0+:OPRD_BW*OPRD_NUM];
    end
    @(posedge clk);
    vld_in =`U_DLY 1'b0;
    oprds =`U_DLY 0;
    rgrs.one_chk_done("num is done.");
end

add_all #(
        .OPRD_BW                        (OPRD_BW                        ),	// bit width of single operand
        .OPRD_NUM                       (OPRD_NUM                       ) 	// number of operands, must 2<=OPRD_NUM<=16
) u_add_all ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .vld_in                         (vld_in                         ),
        .oprds                          (oprds                          ),

        .vld_out                        (                               ),
        .sum_o                          (sum_o                          )
);

initial begin:CHK_SUM
    integer         i;
    integer         sum_chk;

    @(posedge rst_n);

    forever begin
        @(posedge clk);

        sum_chk = 0;
        for(i=0; i<OPRD_NUM; i=i+1) begin
            sum_chk = sum_chk + oprds[i*OPRD_BW+:OPRD_BW];
        end

        if (sum_chk!=sum_o) begin
            $error("sum_chk 0x%h not equal to sum_0 0x%h @oprds 0x%h", sum_chk, sum_o, oprds);
            $stop;
        end
    end
end

endmodule

